﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace ETModel
{
    /// <summary>
    /// 网络通讯组件,支持TCP,KCP,WebSocket三种协议<para/>
    /// 
    /// 如果是作为服务端侦听,则使用:<para/>
    /// AddComponent&lt;NetworkComponent, string&gt;("本地侦听地址");<para/>
    /// 
    /// 如果是作为客户端连接服务端,则使用<para/>
    /// AddComponent&lt;NetworkComponent&gt;().Create("服务端地址");<para/>
    /// </summary>
	public abstract class NetworkComponent : Component
	{
        /// <summary>
        /// Inner时才会自动赋值
        /// </summary>
		public AppType AppType;
		
		protected AService Service;

        /// <summary>
        /// 新的客户端连接事件
        /// </summary>
        public virtual event SessionChannelHandle OnNewClient;

        private readonly Dictionary<long, Session> sessions = new Dictionary<long, Session>();

		public IMessagePacker MessagePacker { get; set; }

		public IMessageDispatcher MessageDispatcher { get; set; }

		public void Awake(NetworkProtocol protocol, int packetSize = Packet.PacketSizeLength4)
		{
			switch (protocol)
			{
				case NetworkProtocol.KCP:
					this.Service = new KService() { Parent = this };
					break;
				case NetworkProtocol.TCP:
					this.Service = new TService(packetSize) { Parent = this };
					break;
				case NetworkProtocol.WebSocket:
					this.Service = new WService() { Parent = this };
					break;
			}
		}

		public virtual void Awake(NetworkProtocol protocol, string address, int packetSize = Packet.PacketSizeLength4)
		{
			try
			{
				IPEndPoint ipEndPoint;
				switch (protocol)
				{
					case NetworkProtocol.KCP:
						ipEndPoint = NetworkHelper.ToIPEndPoint(address);
						this.Service = new KService(ipEndPoint, this.OnAccept) { Parent = this };
						break;
					case NetworkProtocol.TCP:
						ipEndPoint = NetworkHelper.ToIPEndPoint(address);
						this.Service = new TService(packetSize, ipEndPoint, this.OnAccept) { Parent = this };
						break;
					case NetworkProtocol.WebSocket:
						string[] prefixs = address.Split(';');
						this.Service = new WService(prefixs, this.OnAccept) { Parent = this };
						break;
				}
			}
			catch (Exception e)
			{
				throw new Exception($"NetworkComponent Awake Error {address}", e);
			}
		}

        /// <summary>
        /// 当前连接的客户端数量
        /// </summary>
		public int ClientCount
		{
			get { return sessions.Count; }
		}

        /// <summary>
        /// 获取所有客户端待发送的消息数量总和,设计给监控类不频繁的调用场景的
        /// </summary>
        /// <returns></returns>
        public int GetAllClientSendingMsgCount() {
            return sessions.Select(s => s.Value.GetSendingMsgCount()).Sum();
        }

		protected void OnAccept(AChannel channel)
		{
			Session session = ComponentFactory.CreateWithParent<Session, AChannel>(this, channel);
			this.sessions.Add(session.Id, session);
            OnNewClient?.Invoke(session, channel);
            session.Start();
        }

		public virtual void Remove(long id)
		{
			Session session;
			if (!this.sessions.TryGetValue(id, out session))
			{
				return;
			}
			this.sessions.Remove(id);
			session.Dispose();
		}

		public Session Get(long id)
		{
			Session session;
			this.sessions.TryGetValue(id, out session);
			return session;
		}

		/// <summary>
		/// 创建一个新Session
		/// </summary>
		public Session Create(IPEndPoint ipEndPoint)
		{
			AChannel channel = this.Service.ConnectChannel(ipEndPoint);
			Session session = ComponentFactory.CreateWithParent<Session, AChannel>(this, channel);
			this.sessions.Add(session.Id, session);
			session.Start();
			return session;
		}
		
		/// <summary>
		/// 创建一个新Session
		/// </summary>
		public Session Create(string address)
		{
			AChannel channel = this.Service.ConnectChannel(address);
			Session session = ComponentFactory.CreateWithParent<Session, AChannel>(this, channel);
			this.sessions.Add(session.Id, session);
			session.Start();
			return session;
		}

		public void Update()
		{
			if (this.Service == null)
			{
				return;
			}
			this.Service.Update();
		}

		public override void Dispose()
		{
			if (this.IsDisposed)
			{
				return;
			}

			base.Dispose();

			foreach (Session session in this.sessions.Values.ToArray())
			{
				session.Dispose();
			}

			this.Service.Dispose();
		}
	}

    /// <summary>
    /// 会话和通道的处理器
    /// </summary>
    /// <param name="session"></param>
    /// <param name="channel"></param>
    public delegate void SessionChannelHandle(Session session, AChannel channel);
}